/*
 * Copyright (c) 2010-2011 Josef Hardi <josef.hardi@gmail.com>
 * Copyright (c) 2010 Piotr Zagawa <piotr.zagawa@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package id.duapuluhdua.dakidaki.ui;

import java.util.Observable;
import java.util.Observer;

import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Typeface;

import id.duapuluhdua.dakidaki.Command;
import id.duapuluhdua.dakidaki.MainState;
import id.duapuluhdua.dakidaki.Preferences;
import id.duapuluhdua.dakidaki.R;
import id.duapuluhdua.dakidaki.activities.ActivityMain;
import id.duapuluhdua.dakidaki.common.CommonActivity;
import id.duapuluhdua.dakidaki.map.Screen;

/**
 * Abstract element for displaying information in digits.
 */
public abstract class DigitDisplay extends UIElement implements Observer
{
  /** Constants */
  private final int mMargin = 5;
  private final int mPadding = 6;
  private final int mValueSpacing = 2;
  private final int mUnderlineHeight = 2;

  private static int staticDisplayBottom = 0;

  public static final int displayBottomMargin = 16;

  private final Boolean bValueMutex = false;

  /** Fields */
  protected Preferences mPreference;
  protected MainState mState;

  private Paint mTextValue;
  private Paint mTextSuffix;
  private Paint mTextType;
  private Paint mBackground;
  private Paint mUnderline;

  private Rect mRtTextValue;
  private Rect mRtTextSuffix;
  private Rect mRtTextType;

  private LinearGradient mBkgLinearGradient;

  private Typeface mTfBold;

  private String mValueMask;
  private String mSuffixMask;
  private int mValueHeight;
  private int mValueWidth;

  private String mValue;
  private String mSuffix;
  private String mType;

  protected String mDegreeSymbol;

  private boolean bDisplayVisible;

  /**
   *
   * @param parent
   * @param screen
   * @param valueMask
   * @param suffixMask
   * @param typeId
   */
  public DigitDisplay(CommonActivity parent, Screen screen, String valueMask,
      String suffixMask, int typeId)
  {
    super(parent, screen);

    mPreference = parent.getPrefs();
    mState = parent.getMainState();

    mValue = "";
    mSuffix = "";
    mValueMask = valueMask;
    mSuffixMask = suffixMask;
    mType = parent.getString(typeId);

    mDegreeSymbol = parent.getString(R.string.azimuth_degree);

    mRtTextValue = new Rect(0, 0, 0, 0);
    mRtTextSuffix = new Rect(0, 0, 0, 0);
    mRtTextType = new Rect(0, 0, 0, 0);

    mTfBold = Typeface.create((String) null, Typeface.BOLD);

    mTextValue = new Paint();
    mTextValue.setAntiAlias(true);
    mTextValue.setColor(mStyle.textColor1);
    mTextValue.setTextSize(toPixel(mStyle.textSize1));
    mTextValue.setTypeface(mTfBold);
    mTextValue.setTextAlign(Paint.Align.RIGHT);

    mTextSuffix = new Paint();
    mTextSuffix.setAntiAlias(true);
    mTextSuffix.setColor(mStyle.textColor2);
    mTextSuffix.setTextSize(toPixel(mStyle.textSize2));
    mTextSuffix.setTypeface(mTfBold);
    mTextSuffix.setTextSkewX(TEXT_SKEW);

    mTextType = new Paint();
    mTextType.setAntiAlias(true);
    mTextType.setColor(mStyle.textColorUnderline);
    mTextType.setTypeface(mTfBold);
    mTextType.setTextSize(toPixel(mStyle.textSizeUnderline));

    mBackground = new Paint();
    mBackground.setAntiAlias(true);
    mBackground.setColor(mStyle.backgroundColor);

    mUnderline = new Paint();
    mUnderline.setStrokeWidth(1.0f);
    mUnderline.setColor(mStyle.underlineColor);

    updateSize();

    resetValue();

    // Watch DataStats object for data change
    ActivityMain.loader.mStats.addObserver(this);

    // Watch MainState object for UI mode change
    mState.addObserver(this);
  }

  @Override
  public void updateStyle()
  {
    mStyle.backgroundColor = 0xff407080;
    mStyle.backgroundColorDark = 0xff104050;

    mStyle.textColor1 = 0xffffffff;
    mStyle.textColor2 = 0xff6090b0;
    mStyle.textColorUnderline = 0xff70b0c0;
    mStyle.underlineColor = 0xff507090;

    mStyle.textSize1 = 36;
    mStyle.textSize2 = 20;
    mStyle.textSizeUnderline = 10;

    mStyle.backgroundRound = 6.0f;

    mStyle.rtBackgroundMarginCorrect.top = 5;
    mStyle.rtBackgroundMarginCorrect.bottom = 5;
  }

  private void updateSize()
  {
    Paint.FontMetricsInt fm = null;

    // Get value char size for one digit
    mTextValue.getTextBounds("8", 0, 1, mRtTextValue);

    fm = mTextValue.getFontMetricsInt();
    mValueWidth = (mValueMask.length() * mRtTextValue.width());
    mValueHeight = Math.abs(fm.ascent);

    // Get value text bounds for value
    mTextValue.getTextBounds(mValue, 0, mValue.length(), mRtTextValue);

    // Get suffix text rectangle
    if (mSuffixMask != null) {
      mTextSuffix.getTextBounds(mSuffixMask, 0, mSuffixMask.length(), mRtTextSuffix);
    }

    // Get type text rectangle
    updateTypeTextBounds();

    // Initialize sizes
    final int iWidth = mPadding + mValueWidth + mMargin + mRtTextSuffix.width() + mPadding;
    final int iHeight = mMargin + mValueHeight + mMargin + mUnderlineHeight + mRtTextType.height() + mMargin;

    staticDisplayBottom = iHeight;

    setSize(iWidth, iHeight);
  }

  private void updateTypeTextBounds()
  {
    // Get type text rectangle
    mTextType.getTextBounds(mType, 0, mType.length(), mRtTextType);

    Paint.FontMetricsInt fm = mTextType.getFontMetricsInt();
    mRtTextType.top = 0;
    mRtTextType.bottom = Math.abs(fm.ascent);
  }

  @Override
  public void doDraw(Canvas canvas)
  {
    if (!bDisplayVisible) {
      return;
    }

    super.doDraw(canvas);

    final Rect area = canvas.getClipBounds();
    final int displayTop = area.height() - staticDisplayBottom;

    // Place the display panel on the bottom of the screen.
    mRtBackground.top += displayTop;
    mRtBackground.bottom += displayTop + staticDisplayBottom;

    // Draw gradient background
    if (mBkgLinearGradient == null) {
      mBkgLinearGradient = new LinearGradient(mRtBackground.left,
          mRtBackground.top, mRtBackground.left, mRtBackground.bottom,
          mStyle.backgroundColor, mStyle.backgroundColorDark,
          Shader.TileMode.CLAMP);
    }

    this.drawSimpleShadow(canvas, 8);

    mBackground.setShader(mBkgLinearGradient);
    canvas.drawRoundRect(mRtBackground, mStyle.backgroundRound, mStyle.backgroundRound, mBackground);
    mBackground.setShader(null);

    // Draw digits
    final float textValueTop = displayTop + (mRtBounds.top + mMargin + mValueHeight);
    final float textValueRight = mRtBounds.right - mPadding;

    synchronized (bValueMutex) {
      if (mSuffixMask == null) {
        canvas.drawText(mValue, textValueRight, textValueTop, mTextValue); // draw value
      }
      else {
        // Get suffix text rectangle
        mTextSuffix.getTextBounds(mSuffix, 0, mSuffix.length(), mRtTextSuffix);

        // Draw suffix
        canvas.drawText(mSuffix, textValueRight - mRtTextSuffix.width(), textValueTop, mTextSuffix);
        // Draw value
        canvas.drawText(mValue, textValueRight - mRtTextSuffix.width() - mValueSpacing, textValueTop, mTextValue);
      }
    }

    // Draw line
    final int linePosTop = displayTop + (mMargin + mValueHeight + mMargin);

    canvas.drawLine(mRtBounds.left + mPadding, linePosTop, mRtBounds.right - mPadding, linePosTop, mUnderline);

    // Draw type text
    final float lineTextPosLeft = mRtBounds.left + (mRtBounds.width() >> 1) - (mRtTextType.width() >> 1);
    final float lineTextPosTop = linePosTop + mUnderlineHeight * 2 + mRtTextType.height();

    canvas.drawText(mType, lineTextPosLeft, lineTextPosTop, mTextType);
  }

  @Override
  public void surfaceSizeChanged(int width, int height, int screenOrientation)
  {
    // Do nothing!
  }

  public abstract String getZeroValue();

  public void resetValue()
  {
    synchronized (bValueMutex) {
      this.mValue = getZeroValue();
      if (mSuffixMask == null) {
        this.mSuffix = "";
      }
      else {
        this.mSuffix = "...";
      }
    }
  }

  public void setValue(String value, String suffix)
  {
    synchronized (bValueMutex) {
      this.mValue = value;
      this.mSuffix = suffix;
    }
  }

  public void setType(String type)
  {
    this.mType = type;
    updateTypeTextBounds();
  }

  public static int getStaticDisplayBottom()
  {
    return staticDisplayBottom;
  }

  @Override
  public void onClick()
  {
    // Do nothing!
  }

  public void showDisplay()
  {
      bDisplayVisible = true;
  }

  public void hideDisplay()
  {
      bDisplayVisible = false;
  }

  public abstract boolean updateValues();

  public boolean isLocationEnabled()
  {
    return ActivityMain.loader.mStats.isLocationEnabled();
  }

  @Override
  public void update(Observable observable, Object data)
  {
    if (observable == ActivityMain.loader.mStats) {
      if (data.equals(ActivityMain.loader.mStats.bUpdatedLocation)) {
        if (!updateValues()) {
          resetValue();
        }
      }
      if (data.equals(ActivityMain.loader.mStats.bUpdatedMode)) {
        if (!updateValues()) {
          resetValue();
        }
      }
    }

    if (observable == mState) {
      final int uiMode = mState.getCurrentUiMode();

      if (uiMode == Command.CMD_MODE_TRACKS) {
        showDisplay();
      }
      else {
        hideDisplay();
      }
    }
  }
}
